﻿/*
	Date:			05/25/2012
	Author:         Fabien Olousouzian
	License:      	Microsoft Public License (Ms-PL)
	Product:        XNA Shader Composer - Project
*/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Diagnostics;
using Microsoft.Xna.Framework.Content;

namespace Shader_Composer
{
    /// <summary>
    /// Camera class
    /// Free fly
    /// </summary>
    class Camera
    {

        #region Attributes

        private Vector3 _position;
        private Vector3 _target;
        private Matrix _viewMatrix;
        private Matrix _projectionMatrix;

        private float _yaw, _pitch, _roll;
        private float _speed, _ori_speed, _sensibility;
        private float _far, _near;
        private Matrix _cameraRotation;
        private float _vlimit;

        private SpriteFont _font;
        private Vector2 _windowDimension;

        #endregion

        #region Methods

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="windowDimension"></param>
        public Camera(Vector2 windowDimension)
        {
            _windowDimension = windowDimension;
            ResetCamera(windowDimension);
        }

        /// <summary>
        /// Load the font for debug display
        /// </summary>
        /// <param name="content"></param>
        public void Load(ContentManager content)
        {
            _font = content.Load<SpriteFont>(@"Font\FPSFont");
        }

        /// <summary>
        /// Load the font for debug display
        /// </summary>
        /// <param name="content"></param>
        /// <param name="fontName"></param>
        public void Load(ContentManager content, string fontName)
        {
            _font = content.Load<SpriteFont>(fontName);
        }

        /// <summary>
        /// Reset the camera
        /// </summary>
        /// <param name="windowDimension"></param>
        public void ResetCamera(Vector2 windowDimension)
        {
            _far = 500f;
            _near = 0.5f;
            _yaw = 0.0f;
            _pitch = 0.0f;
            _roll = 0.0f;
            _speed = 0f;
            _ori_speed = 0.3f;
            _sensibility = 0.015f;
            _vlimit = 0.7f;
            _position = new Vector3(0, 0, 0);
            _target = new Vector3(0, 0, 0);

            _viewMatrix = Matrix.Identity;
            _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), windowDimension.X / windowDimension.Y, _near, _far);
            _cameraRotation = Matrix.Identity;
        }

        /// <summary>
        /// Update value & matrice
        /// </summary>
        /// <param name="device"></param>
        public void Update(ref Device device)
        {
            EventInput(ref device);
            UpdateViewMatrix();
        }

        /// <summary>
        /// Add Info Debug
        /// </summary>
        public void AddInfoDebug()
        {
            DisplayInfoDebug.Instance.Buffer += "Position: " + _position + "\nTarget: " + _target + "\nSpeed: " + _speed + "\nSensibility: " + _sensibility;
        }

        /// <summary>
        /// Check the input Event
        /// </summary>
        /// <param name="device"></param>
        private void EventInput(ref Device device)
        {
            //Speed
            if (device.CurrentKeyboardState.IsKeyDown(Keys.LeftShift) || device.CurrentGamePadState.IsButtonDown(Buttons.LeftShoulder))
                _speed = _ori_speed + 0.7f;
            else
                _speed = _ori_speed;

            //--Mouse & Keyboard--

            
            if (device.keyJustPressed(Keys.PageUp))
                _sensibility+=0.01f;
            if (device.keyJustPressed(Keys.PageDown))
                _sensibility-=0.01f;

            //Mouse acceleration
            float mscaleX = device.CurrentMouseState.X - (_windowDimension.X / 2);
            float mscaleY = device.CurrentMouseState.Y - (_windowDimension.Y / 2);

            //Mouse
            if (mscaleX < -1)
                _yaw += (_sensibility + Math.Abs(mscaleX / 2400));

            if (mscaleX > 1)
                _yaw += -(_sensibility + Math.Abs(mscaleX / 2400));


            if (mscaleY > 0.2f && !(_position.Y - _target.Y > 0 && Math.Abs(_position.Y - _target.Y) > _vlimit))
                _pitch += -(_sensibility + Math.Abs(mscaleY / 2400));

            if (mscaleY < -0.2f && !(_position.Y - _target.Y < 0 && Math.Abs(_position.Y - _target.Y) > _vlimit))
                _pitch += (_sensibility + Math.Abs(mscaleY / 2400));


            //Keyboard
            if (device.CurrentKeyboardState.IsKeyDown(Keys.U))
                _roll += -.02f;
            if (device.CurrentKeyboardState.IsKeyDown(Keys.O))
                _roll += .02f;

            if (device.CurrentKeyboardState.IsKeyDown(Keys.W))
                MoveCamera(_cameraRotation.Forward);
            if (device.CurrentKeyboardState.IsKeyDown(Keys.S))
                MoveCamera(-_cameraRotation.Forward);
            if (device.CurrentKeyboardState.IsKeyDown(Keys.A))
                MoveCamera(-_cameraRotation.Right);
            if (device.CurrentKeyboardState.IsKeyDown(Keys.D))
                MoveCamera(_cameraRotation.Right);

            //Up & Down
            if (device.CurrentKeyboardState.IsKeyDown(Keys.E))
                MoveCamera(_cameraRotation.Up);
            if (device.CurrentKeyboardState.IsKeyDown(Keys.Q))
                MoveCamera(-_cameraRotation.Up);


            //--GamePad--

            //LeftStick
            float gscaleX = device.CurrentGamePadState.ThumbSticks.Right.X / 90;
            float gscaleY = device.CurrentGamePadState.ThumbSticks.Right.Y / 90;

            if (device.CurrentGamePadState.ThumbSticks.Right.X < -0.2f)
                _yaw += (_sensibility + Math.Abs(gscaleX));

            if (device.CurrentGamePadState.ThumbSticks.Right.X > 0.2f)
                _yaw += -(_sensibility + Math.Abs(gscaleX));

            if (device.CurrentGamePadState.ThumbSticks.Right.Y > 0.2f && !(_position.Y - _target.Y < 0 && Math.Abs(_position.Y - _target.Y) > _vlimit))
                _pitch += (_sensibility + Math.Abs(gscaleY));

            if (device.CurrentGamePadState.ThumbSticks.Right.Y < -0.2f && !(_position.Y - _target.Y > 0 && Math.Abs(_position.Y - _target.Y) > _vlimit))
                _pitch += -(_sensibility + Math.Abs(gscaleY));

            //RightStick

            if (device.CurrentGamePadState.ThumbSticks.Left.Y > 0.2f)
                MoveCamera(_cameraRotation.Forward);
            if (device.CurrentGamePadState.ThumbSticks.Left.Y < -0.2f)
                MoveCamera(-_cameraRotation.Forward);
            if (device.CurrentGamePadState.ThumbSticks.Left.X < -0.2f)
                MoveCamera(-_cameraRotation.Right);
            if (device.CurrentGamePadState.ThumbSticks.Left.X > 0.2f)
                MoveCamera(_cameraRotation.Right);

            //Up & Down
            if (device.CurrentGamePadState.Triggers.Right > 0.2f)
                MoveCamera(_cameraRotation.Up);
            if (device.CurrentGamePadState.Triggers.Left > 0.2f)
                MoveCamera(-_cameraRotation.Up);
        }

        /// <summary>
        /// Update the camera Matrix
        /// </summary>
        private void UpdateViewMatrix()
        {
            _cameraRotation.Forward.Normalize();
            _cameraRotation.Up.Normalize();
            _cameraRotation.Right.Normalize();

            _cameraRotation *= Matrix.CreateFromAxisAngle(_cameraRotation.Right, _pitch);
            _cameraRotation *= Matrix.CreateFromAxisAngle(_cameraRotation.Up, _yaw);
            _cameraRotation *= Matrix.CreateFromAxisAngle(_cameraRotation.Forward, _roll);

            _yaw = 0.0f;
            _pitch = 0.0f;
            _roll = 0.0f;

          
            _target = _position + _cameraRotation.Forward;

            _viewMatrix = Matrix.CreateLookAt(_position, _target, _cameraRotation.Up);
            _viewMatrix = Matrix.CreateLookAt(_position, _target, Vector3.Up);
        }

        /// <summary>
        /// Move Camera
        /// </summary>
        /// <param name="addedVector"></param>
        private void MoveCamera(Vector3 addedVector)
        {
            _position += _speed * addedVector;
        }

        #endregion

        #region Getter & Setter

        /// <summary>
        /// Get & set the window Dimension
        /// </summary>
        public Vector2 WindowDimension
        {
            get { return _windowDimension; }
            set { _windowDimension = value; }
        }

        /// <summary>
        /// ViewMatrix
        /// </summary>
        public Matrix ViewMatrix
        {
            get { return _viewMatrix; }
            set { _viewMatrix = value; }
        }

        /// <summary>
        /// ProjectionMatrix
        /// </summary>
        public Matrix ProjectionMatrix
        {
            get { return _projectionMatrix; }
            set { _projectionMatrix = value; }
        }

        /// <summary>
        /// Position
        /// </summary>
        public Vector3 Position
        {
            get { return _position; }
            set { _position = value; }
        }

        /// <summary>
        /// Target
        /// </summary>
        public Vector3 Target
        {
            get { return _target; }
            set { _target = value; }
        }

        /// <summary>
        /// Sensibiliy (only for the mouse)
        /// </summary>
        public uint Sensibility
        {
            get { return (uint)_sensibility; }
            set { _sensibility = value / 100f; }
        }

        /// <summary>
        /// Vertical Limit
        /// </summary>
        public float VerticalLimit
        {
            get { return _vlimit; }
            set { _vlimit = value; }
        }

        #endregion
    }
}
